#include "io.h"
#include "cgmmap.h"

/*
 * Jestlize je nastaven mmap a offset je uvnitr cg, tak je v pameti.
 */
int
is_blockinmem(GLBVAL *glb, off_t offset)
{
	off_t cgstart;
	off_t cgstop;
	
	if (glb->flags & GLBCGISMMAP) {
		cgstart = (off_t) cgtod(&afs, (glb->acg)->cg_cgx);
		cgstop = (off_t) cgtod(&afs, (glb->acg)->cg_cgx + 1);
		
		if (offset >= cgstart && offset < cgstop)
			return 1;
	}
	
	return 0;
}

/*
 * Precte fs block a ulozi do buf nebo pokud mmap tak zmeni ukazatel v buf
 * do oblasti mmapu.
 */
int
read_fs_block (GLBVAL *glb, void **buf, off_t offset)
{
	/* Cteni z pameti (mmap) */
	if (is_blockinmem(glb, offset)) {
		cgm_read_fs_block(glb, buf, offset);
	}
	/* Cteni z disku */
	else {
		offset = fsbtodb(&afs, offset); 	/* fs blk disk blk */
		offset *= DEV_BSIZE; 				/* disk blk to bytes */
		
		if (pread(glb->fd, *buf, afs.fs_bsize, offset) != afs.fs_bsize) {
			warn("Can not read fs block");
			return 1;
		}
	}
	
	return 0;
}

/* 
 * Zapise fs blok
 */
int
write_fs_block (GLBVAL *glb, char *buf, off_t offset)
{	
	if (is_blockinmem(glb, offset)) {
		return cgm_write_fs_block(glb, buf, offset);
	}
	else {
		offset = fsbtodb(&afs, offset);		/* fs blk disk blk */
		offset *= DEV_BSIZE; 				/* disk blk to bytes */
		
		if (pwrite(glb->fd, buf, afs.fs_bsize, offset) != afs.fs_bsize) {
			warn("Can not write fs block");
			return 1;
		}
	}
	return 0;
}


/* 
 * Nacte do buf blok s inody
 */
int
read_inodes_block(GLBVAL *glb, void **buf, ino_t inum)
{
	off_t offset;
	int ret;

	offset = ino_to_fsba(&afs, inum);	/* inode to fs blk */
	ret = read_fs_block(glb, buf, offset);
	
	return ret;
}

/* 
 * Zapise blok s inody
 */
int
write_inodes_block(GLBVAL *glb, char *buf, ino_t inum)
{
	off_t offset;
	int ret;
	
	offset = ino_to_fsba(&afs, inum);	/* inode to fs blk */
	ret = write_fs_block(glb, buf, offset);
	return ret;
}



/*
 *  Otevre zarizeni a precte super block 
 */
int
open_disk(const char *name)
{
    int 	fd;
    int 	i;
    int 	sbtry[] = SBLOCKSEARCH;
    ssize_t n;

    if ((fd = open(name, O_RDWR, 0)) < 0) {
        fprintf(stderr, "Can not open device %s\n", name);
        return -1;
    }

    /* cte superblock, muze byt UFS1 nebo UFS2. */
    for (i = 0; sbtry[i] != -1; i++) {
        n = pread(fd, &afs, SBLOCKSIZE, (off_t)sbtry[i]);
        if (n == SBLOCKSIZE && (afs.fs_magic == FS_UFS1_MAGIC ||
            (afs.fs_magic == FS_UFS2_MAGIC &&
            afs.fs_sblockloc == sbtry[i])) &&
            afs.fs_bsize <= MAXBSIZE &&
            afs.fs_bsize >= sizeof(struct fs))
            break;
    }
    
    /* nic se nenaslo */
    if (sbtry[i] == -1) {
        fprintf(stderr, "Can not find filesystem superblock\n");
        close(fd);
        return -1;
    }
    /* UFS2 neni podporovano */
    else if (afs.fs_magic == FS_UFS2_MAGIC) {
        fprintf(stderr, "UFS2 is not supported yet\n");
        close(fd);
        return -1;
	}

    return fd;
}


/* 
 * Cylinder Group 
 * Precte cg do bufferu acg.
 * 
 * Jelikoz volane rutiny jiz pouzivaji nodnoty z cg (cg_cgx), tak 
 * pokud se jedna o prvni nacteni aktualniho cg, pak musim nastavit
 * zde nastavit na hodnotu ncyl. Vim ze ukazatel musi ukazavat
 * vzdy na alokovane misto.
 */
int 
read_cgblock(GLBVAL *glb, struct cg **acg, int ncyl) 
{
	off_t 	offset;
	int 	ret;
	
	/* 
	 * Pokud ukazuju na stejne misto, nastavuji acg v glb (prvni cteni) 
	 * a v pripade mmap pouzivam jiz acg->cg_cgx.
	 */
	if (glb->acg == *acg)
		((struct cg *) *acg)->cg_cgx = ncyl;
	
	offset = cgtod(&afs, ncyl);
	ret = read_fs_block(glb, (void **) acg, offset);	
	return ret;
}

/*
 * pro prvni cteni cg s mmapem musim mit samostatnou fci..
 * duvodem je mmap, pro alokaci prostoru potrebuju vedet,
 * kolik bloku je v aktualnim cg - posledni ma vetsinou mene
 */
int 
read_cgblock_ft(GLBVAL *glb, struct cg **acg, int ncyl) 
{
	off_t	offset;
	
	offset = cgtod(&afs, ncyl);
	offset = fsbtodb(&afs, offset);
	offset *= DEV_BSIZE;
	
	if (pread(glb->fd, (void **) *acg, afs.fs_bsize, offset) != afs.fs_bsize) {
		warn("Can not read cg block - first time\n");
		return 1;
	}
	
	return 0;
}



/* 
 * Ulozi cg.
 */
int 
write_cgblock(GLBVAL *glb) 
{
	off_t 	offset;
	int 	ret;
	
	offset = cgtod(&afs, (glb->acg)->cg_cgx);
	ret = write_fs_block(glb, (char *) glb->acg, offset);
	
	return ret;
}
